package cn.blnp.net.flowable.event.middle.conditional;

import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <h3>中间事件-条件中间捕获事件</h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/5/12 9:09
 */
@Slf4j
@SpringBootTest
public class MidConditionalEvent {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;

    /**
     *  <p>
     *      条件中间捕获事件指在流程中将一个条件事件作为独立的节点来运行，是一种捕获事件。当流程执行到条件中间事件时会<b>暂停</b>，等待接收到符合条件的事件触发后，流程沿外出顺序流继续流转。
     *  具体事件定义格式如下所示：
     *  <pre>
     *      {@code
     *          <intermediateCatchEvent id="conditionalIntermediateCatchEvent1">
     *              <!-- 包含conditionalEventDefinition子元素，代表条件中间捕获事件 -->
     *              <conditionalEventDefinition>${condition==true}</conditionalEventDefinition>
     *          </intermediateCatchEvent>
     *      }
     *  </pre>
     *  </p>
     *  <h2>案例演示</h2>
     *  <hr/>
     *  <p>
     *      该流程为奖项评审流程：提名提交后，首先到达条件中间捕获事件，当投票数到达5的时候，进入“材料审核”任务节点，如果判断材料齐全，则流转到“结果公示”任务节点；如果判断材料不齐全，则进入
     *  “补充材料”任务节点，完成后再回到“材料审核”任务节点。结果公示完成时触发条件发起发证事件子流程。<br/>
     *  <b>流程定义图：</b>{@link flow/midConditionalEvent.bpmn20.xml}<br/>
     *  <b>流程预览图：</b><br/>
     *  <img src="https://cdn4.winhlb.com/2025/05/12/68214d2583fe7.png" />
     *  </p>
     **/
    @Test
    public void test() {
        Deployment deploy = repositoryService.createDeployment()
                .name("条件中间捕获事件案例")
                .addClasspathResource("flow/midConditionalEvent.bpmn20.xml")
                .deploy();
        log.info("部署流程定义：{}", deploy.getId());
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult();
        log.info("流程定义编号：{}", procDef.getId());
        //启动流程实例
        ProcessInstance procInst = runtimeService.startProcessInstanceById(procDef.getId());
        TaskQuery taskQuery = taskService.createTaskQuery()
                .processInstanceId(procInst.getId());
        //查询并办理第一个用户任务
        Task firstTask = taskQuery.singleResult();
        log.info("流转到第1个Task：{}", firstTask.getName());
        taskService.complete(firstTask.getId());

        //条件捕获阻挡等待统计
        Task secondTask = null;
        int voteNum = 1;
        do {
            //设置变量并计算条件
            Map<String, Object> varMap1 = MapUtil.builder(new HashMap<String, Object>())
                    .put("voteNum", voteNum)
                    .build();
            //表达式计算
            runtimeService.evaluateConditionalEvents(procInst.getId(), varMap1);
            //查询第二个用户任务
            secondTask = taskQuery.singleResult();
            if (secondTask == null) {
                log.info("voteNum=={}时没有触发条件中间捕获事件", voteNum);
                voteNum++;
            } else {
                log.info("voteNum=={}时触发条件中间捕获事件，流程流转到第2个Task：{}", voteNum, secondTask.getName());
                break;
            }
        } while (true);

        //材料审核检查，设置变量并计算条件
        Map<String, Object> varMap2 = MapUtil.builder(new HashMap<String, Object>())
                .put("materialState", false)
                .build();
        runtimeService.evaluateConditionalEvents(procInst.getId(), varMap2);

        //查询并办理第三个用户任务
        Task thirdTask = taskQuery.singleResult();
        log.info("materialState==false时触发条件边界事件，流程流转到第3个Task：{}", thirdTask.getName());
        taskService.complete(thirdTask.getId());
        //此时是材料不足打回，需重新提交审核
        List<Task> taskList = taskQuery.list();
        for (Task task : taskList) {
            log.info("流程流转到第4个Task：{}", task.getName());
            taskService.complete(task.getId());
        }

        //查询第五个用户任务,进行结果公示
        Task fifthTask = taskQuery.singleResult();
        log.info("流程流转到第5个Task：{}", fifthTask.getName());

        //设置变量并计算条件，确认公示无误可制作证书
        Map<String, Object> varMap3 = MapUtil.builder(new HashMap<String, Object>())
                .put("result", "success")
                .build();
        runtimeService.evaluateConditionalEvents(procInst.getId(), varMap3);
        //办理第五个用户任务
        taskService.complete(fifthTask.getId());

        //查询第六个用户任务，进行制作证书
        Task sixTask = taskQuery.singleResult();
        log.info("流程流转到第6个Task：{}", sixTask.getName());

        /**
         *  演示结果：
         * 2025-05-12 09:37:10.583  INFO 22104 --- [           main] c.b.n.f.e.m.c.MidConditionalEvent        : 部署流程定义：9f05b7d3-2ed1-11f0-845b-50ebf6ec8f6d
         * 2025-05-12 09:37:10.586  INFO 22104 --- [           main] c.b.n.f.e.m.c.MidConditionalEvent        : 流程定义编号：midConditionalEvent:1:9f63dd66-2ed1-11f0-845b-50ebf6ec8f6d
         * 2025-05-12 09:37:10.665  INFO 22104 --- [           main] c.b.n.f.e.m.c.MidConditionalEvent        : 流转到第1个Task：提名
         * 2025-05-12 09:37:10.737  INFO 22104 --- [           main] c.b.n.f.e.m.c.MidConditionalEvent        : voteNum==1时没有触发条件中间捕获事件
         * 2025-05-12 09:37:10.757  INFO 22104 --- [           main] c.b.n.f.e.m.c.MidConditionalEvent        : voteNum==2时没有触发条件中间捕获事件
         * 2025-05-12 09:37:10.788  INFO 22104 --- [           main] c.b.n.f.e.m.c.MidConditionalEvent        : voteNum==3时没有触发条件中间捕获事件
         * 2025-05-12 09:37:10.807  INFO 22104 --- [           main] c.b.n.f.e.m.c.MidConditionalEvent        : voteNum==4时没有触发条件中间捕获事件
         * 2025-05-12 09:37:10.841  INFO 22104 --- [           main] c.b.n.f.e.m.c.MidConditionalEvent        : voteNum==5时触发条件中间捕获事件，流程流转到第2个Task：材料审核
         * 2025-05-12 09:37:10.891  INFO 22104 --- [           main] c.b.n.f.e.m.c.MidConditionalEvent        : materialState==false时触发条件边界事件，流程流转到第3个Task：补充材料
         * 2025-05-12 09:37:10.928  INFO 22104 --- [           main] c.b.n.f.e.m.c.MidConditionalEvent        : 流程流转到第4个Task：材料审核
         * 2025-05-12 09:37:10.982  INFO 22104 --- [           main] c.b.n.f.e.m.c.MidConditionalEvent        : 流程流转到第5个Task：结果公示
         * 2025-05-12 09:37:11.070  INFO 22104 --- [           main] c.b.n.f.e.m.c.MidConditionalEvent        : 流程流转到第6个Task：制作证书
         **/
    }
}
